/**
 * Copyright © 2010 DocuLibre inc.
 * 
 * This file is part of Constellio.
 * 
 * Constellio is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Constellio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Constellio.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.doculibre.constellio.connector.persistence.services;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import com.doculibre.constellio.connector.persistence.BaseEntity;
import com.doculibre.constellio.connector.persistence.config.PersistenceContext;
import com.doculibre.constellio.connector.persistence.services.BaseCRUDServices;


/**
 * Default implementation for CRUD services.
 * 
 * @author Nicolas Bélisle (nicolas.belisle@doculibre.com)
 * 
 * @param <T>
 */
public abstract class BaseCRUDServicesImpl<T extends BaseEntity> implements
		BaseCRUDServices<T> {

	protected Class<T> entityClass;

	private PersistenceContext persistenceContext;

	public BaseCRUDServicesImpl(Class<T> entityClass,
			PersistenceContext persistenceContext) {
		this.entityClass = entityClass;
		this.persistenceContext = persistenceContext;
	}

	@Override
	public void clear() {
		getEntityManager().clear();
	}

	@Override
	public void flush() {
		getEntityManager().flush();
	}

	@SuppressWarnings("unchecked")
	@Override
	public T get(Long id) {
		Query query = getEntityManager().createQuery(
				"from " + entityClass.getName() + " o where id=:id");
		query.setParameter("id", id);
		query.setMaxResults(1);
		return (T) query.getSingleResult();
	}

	@SuppressWarnings("unchecked")
	@Override
	public T get(Map<String, Object> criteria) {
		StringBuffer sb = new StringBuffer("from " + entityClass.getName()
				+ " o");
		if (criteria != null && !criteria.isEmpty()) {
			sb.append(" where ");
			int index = 0;
			for (String propertyName : criteria.keySet()) {
				sb.append("o." + propertyName + "= ?" + index++);
			}
		}
		Query query = getEntityManager().createQuery(sb.toString());
		if (criteria != null && !criteria.isEmpty()) {
			int index = 0;
			for (Object propertyValue : criteria.values()) {
				query.setParameter(index++, propertyValue);
			}
		}
		try {
			return (T) query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	public final EntityManager getEntityManager() {
		return this.persistenceContext.getCurrentEntityManager();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> list() {
		Query query = getEntityManager().createQuery(
				"from " + entityClass.getName() + " o");
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> list(Map<String, Object> criteria) {
		StringBuffer sb = new StringBuffer("from " + entityClass.getName()
				+ " o");
		if (criteria != null && !criteria.isEmpty()) {
			sb.append(" where ");
			int index = 0;
			for (String propertyName : criteria.keySet()) {
				sb.append("o." + propertyName + "= ?" + index++);
			}
		}
		Query query = getEntityManager().createQuery(sb.toString());
		if (criteria != null && !criteria.isEmpty()) {
			int index = 0;
			for (Object propertyValue : criteria.values()) {
				query.setParameter(index++, propertyValue);
			}
		}
		return (List<T>) query.getResultList();
	}

	@Override
	public T makePersistent(T entity) {
		getEntityManager().persist(entity);
		return entity;
	}

	@Override
	public T makeTransient(T entity) {
		getEntityManager().remove(entity);
		return entity;
	}

	@Override
	public T merge(T entity) {
		getEntityManager().merge(entity);
		return entity;
	}

	@Override
	public void refresh(T entity) {
		getEntityManager().refresh(entity);
	}
}
